/*
 * Copyright 2002-2011 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.ternnetwork.auth.server.config;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.ternnetwork.core.accesscontroll.entity.User;
import com.ternnetwork.core.accesscontroll.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

/**
 * 可以进行自定义的认证，比如说第三方的认证，ldap等
 * @author Dave Syer
 * 
 */
public class CustomResourceOwnerPasswordTokenGranter extends AbstractTokenGranter {

	private static final String GRANT_TYPE = "password";
	
	private static final String PARAMETER_PASSWORD="password";
	
	private static final String PARAMETER_USERNAME="username";

	private static final String PARAMETER_OPEN_ID ="openId";

	private static final String PARAMETER_THIRD_PARTY ="thirdParty";

	private static final String THIRD_PARTY_AUTH_WECHAT_APP="WeChatApp";

	private final AuthenticationManager authenticationManager;


	@Autowired
	private UserRepository userRepository;

	public CustomResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager,
												   AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
		this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);

	}

	protected CustomResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
			ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
		super(tokenServices, clientDetailsService, requestFactory, grantType);
		this.authenticationManager = authenticationManager;
	}

	@Override
	protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {

		Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
		String username = parameters.get(PARAMETER_USERNAME);
		String password = parameters.get(PARAMETER_PASSWORD);
		String thirdPartyAuth = parameters.get(PARAMETER_THIRD_PARTY);
		// Protect from downstream leaks of password
		parameters.remove(PARAMETER_PASSWORD);

		Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password,true);
		
		((AbstractAuthenticationToken) userAuth).setDetails(parameters);
		
		//如果是进行ldap验证
		if(THIRD_PARTY_AUTH_WECHAT_APP.equals(thirdPartyAuth)) {

			String openId = parameters.get(PARAMETER_OPEN_ID);

			List<User> userList=userRepository.findAllByWechatId(openId);

			if(userList.size()>0){

				username=userList.get(0).getName();

			}
			//这里先进行第三方的认证，如果成功继续往下走，否则抛出异常
			
			userAuth = new UsernamePasswordAuthenticationToken(username, password,false);
		
			((AbstractAuthenticationToken) userAuth).setDetails(parameters);
		}

		try {
			
			userAuth = authenticationManager.authenticate(userAuth);
		}
		catch (AccountStatusException ase) {
			//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
			throw new InvalidGrantException(ase.getMessage());
		}
		catch (BadCredentialsException e) {
			// If the username/password are wrong the spec says we should send 400/invalid grant
			throw new InvalidGrantException(e.getMessage());
		}
		if (userAuth == null || !userAuth.isAuthenticated()) {
			throw new InvalidGrantException("Could not authenticate user: " + username);
		}
		
		OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);		
		return new OAuth2Authentication(storedOAuth2Request, userAuth);
	}
}
